/**
 * 认证服务实现类
 * 
 * 功能说明：
 * - 用户登录认证：验证用户身份，生成JWT令牌
 * - 用户注册：创建新用户，分配默认角色，同步用户数据
 * - 用户信息管理：修改密码、更新个人资料
 * - 用户角色权限管理：维护用户与角色的关联关系
 * - 跨服务数据同步：通过Feign客户端与其他微服务保持数据一致性
 * 
 * 业务流程：
 * 1. 登录流程：验证用户名密码 → 检查账户状态 → 更新登录时间 → 生成JWT令牌 → 返回用户信息
 * 2. 注册流程：验证数据唯一性 → 创建用户记录 → 分配默认角色 → 同步到用户服务 → 返回认证信息
 * 3. 密码修改：验证原密码 → 加密新密码 → 更新用户记录 → 同步到其他服务
 * 4. 资料更新：验证用户权限 → 更新用户信息 → 同步到用户服务
 * 
 * 数据一致性：
 * - 使用Spring事务管理确保数据库操作的原子性
 * - 通过Feign客户端调用用户服务进行数据同步
 * - 异步处理同步失败情况，记录日志但不影响主流程
 * - 支持最终一致性模型，允许短暂的数据不一致
 * 
 * 安全控制：
 * - 密码使用BCrypt加密算法进行单向加密存储
 * - 支持账户锁定机制，防止暴力破解
 * - 支持账户禁用功能，用于风险控制
 * - JWT令牌包含用户ID、用户名、角色列表等关键信息
 * - 不区分用户名不存在和密码错误，统一返回通用错误信息
 * 
 * 性能优化：
 * - 使用@Slf4j注解记录详细的业务日志
 * - 合理的异常处理机制，避免敏感信息泄露
 * - 支持用户信息的懒加载和缓存机制
 * 
 * 微服务集成：
 * - 通过UserServiceClient调用用户服务进行数据同步
 * - 支持用户信息的跨服务共享和一致性维护
 * - 处理网络异常和服务不可用等异常情况
 * 
 * @author eCommerce Team
 * @version 1.0
 */
package com.ecommerce.auth.service.impl;

import com.ecommerce.auth.client.UserServiceClient;
import com.ecommerce.auth.config.KafkaConfig;
import com.ecommerce.auth.dto.*;
import com.ecommerce.auth.entity.Role;
import com.ecommerce.auth.entity.User;
import com.ecommerce.auth.entity.UserRole;
import com.ecommerce.auth.repository.RoleRepository;
import com.ecommerce.auth.repository.UserRepository;
import com.ecommerce.auth.repository.UserRoleRepository;
import com.ecommerce.auth.service.AuthService;
import com.ecommerce.auth.util.JwtUtil;
import com.ecommerce.common.dto.Result;
import com.ecommerce.common.event.UserDeletedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 认证服务实现类
 * 实现用户认证、注册、信息管理等核心业务逻辑
 */
@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    /**
     * Spring Security认证管理器
     * 用于用户身份验证，验证用户名和密码的正确性
     */
    private final AuthenticationManager authenticationManager;

    /**
     * 用户数据访问层
     * 负责用户表的CRUD操作，包含用户名、密码、邮箱等基本信息
     */
    private final UserRepository userRepository;

    /**
     * 角色数据访问层
     * 负责角色表的CRUD操作，包含角色名称、描述等信息
     */
    private final RoleRepository roleRepository;

    /**
     * 用户角色关联数据访问层
     * 负责用户与角色多对多关系的维护
     */
    private final UserRoleRepository userRoleRepository;

    /**
     * 密码加密器
     * 使用BCrypt算法对用户密码进行加密和验证
     * 确保密码在数据库中以密文形式存储
     */
    private final PasswordEncoder passwordEncoder;

    /**
     * JWT工具类
     * 负责JWT令牌的生成、解析和验证
     * 包含访问令牌和刷新令牌的创建逻辑
     */
    private final JwtUtil jwtUtil;

    /**
     * 用户服务Feign客户端
     * 用于调用用户服务，实现用户数据的跨服务同步
     * 确保用户数据在认证服务和用户服务之间保持一致
     */
    private final UserServiceClient userServiceClient;

    /**
     * 用户删除事件生产者
     * 用于向Kafka发送用户删除事件，通知其他微服务同步删除用户数据
     * 确保用户数据在所有相关服务中保持一致性
     */
    private final KafkaConfig.UserDeletedEventProducer userDeletedEventProducer;

    /**
     * 构造方法
     * 
     * 使用构造器注入方式，确保所有依赖项都是必需的
     * 这种注入方式的优势：
     * - 保证依赖不为null，提高代码健壮性
     * - 便于单元测试，可以轻松注入mock对象
     * - 符合Spring官方推荐的注入方式
     * 
     * @param authenticationManager Spring Security认证管理器
     * @param userRepository 用户数据访问层
     * @param roleRepository 角色数据访问层
     * @param userRoleRepository 用户角色关联数据访问层
     * @param passwordEncoder 密码加密器
     * @param jwtUtil JWT工具类
     * @param userServiceClient 用户服务Feign客户端
     * @param userDeletedEventProducer 用户删除事件生产者
     */
    @Autowired
    public AuthServiceImpl(AuthenticationManager authenticationManager, UserRepository userRepository,
                         RoleRepository roleRepository, UserRoleRepository userRoleRepository,
                         PasswordEncoder passwordEncoder, JwtUtil jwtUtil, UserServiceClient userServiceClient,
                         KafkaConfig.UserDeletedEventProducer userDeletedEventProducer) {
        this.authenticationManager = authenticationManager;
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.userRoleRepository = userRoleRepository;
        this.passwordEncoder = passwordEncoder;
        this.jwtUtil = jwtUtil;
        this.userServiceClient = userServiceClient;
        this.userDeletedEventProducer = userDeletedEventProducer;
    }

    /**
     * 用户登录方法
     * 
     * 登录流程详细说明：
     * 1. 根据用户名查询用户信息
     * 2. 使用BCrypt验证密码的正确性
     * 3. 检查账户状态（是否被锁定、是否启用）
     * 4. 更新最后登录时间
     * 5. 获取用户角色列表
     * 6. 生成JWT访问令牌和刷新令牌
     * 7. 返回完整的认证响应信息
     * 
     * 安全考虑：
     * - 不区分用户名不存在和密码错误的具体原因，统一返回"用户名或密码错误"
     * - 密码验证使用BCrypt的matches方法，防止时序攻击
     * - 账户状态检查在密码验证之后，避免用户枚举攻击
     * 
     * @param loginRequest 登录请求对象，包含用户名和密码
     * @return AuthResponse 认证响应，包含令牌和用户信息
     * @throws RuntimeException 当用户名密码错误、账户被锁定或禁用时抛出
     */
    @Override
    public AuthResponse login(LoginRequest loginRequest) {
        try {
            // 验证用户名和密码
            User user = userRepository.findByUsername(loginRequest.getUsername())
                    .orElseThrow(() -> new RuntimeException("用户名或密码错误"));

            // 验证密码
            if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
                throw new RuntimeException("用户名或密码错误");
            }

            // 检查用户是否被锁定
            if (user.getLocked()) {
                throw new RuntimeException("账户已被锁定，请联系管理员");
            }

            // 检查用户是否启用
            if (!user.getEnabled()) {
                throw new RuntimeException("账户已被禁用，请联系管理员");
            }

            // 更新最后登录时间
            user.setLastLoginTime(LocalDateTime.now());
            userRepository.save(user);

            // 获取用户角色
            List<String> roles = userRoleRepository.findRoleNamesByUserId(user.getId());

            // 生成令牌
            String token = jwtUtil.generateToken(user.getUsername(), user.getId(), roles, user.getUserType().name());
            String refreshToken = jwtUtil.generateRefreshToken(user.getUsername(), user.getId());

            return new AuthResponse(
                    token,
                    refreshToken,
                    user.getId(),
                    user.getUsername(),
                    user.getEmail(),
                    user.getPhoneNumber(),
                    user.getNickName(),
                    user.getAddress(),
                    user.getAvatarUrl(),
                    user.getGender().name(),
                    user.getLastLoginTime(),
                    roles,
                    user.getUserType().name()
            );
        } catch (Exception e) {
            throw new RuntimeException("登录失败: " + e.getMessage());
        }
    }

/**
     * 用户注册方法
     * 
     * 注册流程详细说明：
     * 1. 数据唯一性验证：检查用户名、邮箱、手机号是否已存在
     * 2. 创建用户实体：设置基本信息，密码使用BCrypt加密
     * 3. 分配默认角色：为新用户分配ROLE_USER角色
     * 4. 数据持久化：保存用户和角色关联信息
     * 5. 跨服务同步：通过Feign客户端同步数据到用户服务
     * 6. 生成认证令牌：创建JWT访问令牌和刷新令牌
     * 7. 返回注册结果：包含令牌和用户基本信息
     * 
     * 事务管理：
     * - 使用@Transactional注解确保数据库操作的原子性
     * - 所有数据库操作要么全部成功，要么全部回滚
     * - 跨服务同步失败不影响主流程，只记录日志
     * 
     * 数据验证：
     * - 用户名唯一性检查：防止重复注册
     * - 邮箱唯一性检查：确保邮箱地址的唯一性
     * - 手机号唯一性检查：仅当手机号不为空时进行验证
     * - 密码加密存储：使用BCrypt算法进行单向加密
     * 
     * 角色分配：
     * - 为新用户分配ROLE_USER默认角色
     * - 如果角色不存在则自动创建
     * - 角色信息用于后续的权限控制
     * 
     * 跨服务同步：
     * - 通过UserServiceClient同步用户数据到用户服务
     * - 采用异步处理方式，不影响主流程
     * - 同步失败时记录错误日志，不影响注册成功
     * 
     * @param registerRequest 注册请求对象，包含用户注册信息
     * @return AuthResponse 认证响应，包含令牌和用户信息
     * @throws RuntimeException 当用户名、邮箱或手机号已存在时抛出
     */
    @Override
    @Transactional
    public AuthResponse register(RegisterRequest registerRequest) {
        log.info("开始用户注册流程，用户名: {}", registerRequest.getUsername());
        
        // 检查用户名是否已存在
        if (userRepository.existsByUsername(registerRequest.getUsername())) {
            log.warn("注册失败，用户名已存在: {}", registerRequest.getUsername());
            throw new RuntimeException("用户名已存在");
        }

        // 检查邮箱是否已存在
        if (userRepository.existsByEmail(registerRequest.getEmail())) {
            log.warn("注册失败，邮箱已存在: {}", registerRequest.getEmail());
            throw new RuntimeException("邮箱已存在");
        }

        // 检查手机号是否已存在（仅当手机号不为空时检查）
        if (registerRequest.getPhoneNumber() != null && 
            !registerRequest.getPhoneNumber().trim().isEmpty() && 
            userRepository.existsByPhoneNumber(registerRequest.getPhoneNumber())) {
            log.warn("注册失败，手机号已存在: {}", registerRequest.getPhoneNumber());
            throw new RuntimeException("手机号已存在");
        }

        // 创建新用户
        User user = new User();
        user.setUsername(registerRequest.getUsername());
        
        // 检测密码是否已经加密（BCrypt密码以$2a$开头）
        String password = registerRequest.getPassword();
        if (password != null && password.startsWith("$2a$")) {
            // 密码已经加密，直接使用
            user.setPassword(password);
            log.info("检测到已加密的密码，直接使用，用户名: {}", registerRequest.getUsername());
        } else {
            // 密码未加密，进行加密
            user.setPassword(passwordEncoder.encode(password));
            log.info("密码未加密，进行BCrypt加密，用户名: {}", registerRequest.getUsername());
        }
        
        user.setEmail(registerRequest.getEmail());
        user.setPhoneNumber(registerRequest.getPhoneNumber());
        user.setNickName(registerRequest.getNickName() != null ? registerRequest.getNickName() : registerRequest.getUsername());
        user.setAddress(registerRequest.getAddress());
        user.setEnabled(true);
        user.setLocked(false);
        user.setUserType(User.UserType.CUSTOMER);
        user.setGender(User.Gender.UNKNOWN);
        user.setAuthProvider(User.AuthProvider.LOCAL);

        // 保存用户
        user = userRepository.save(user);
        log.info("用户创建成功，用户ID: {}", user.getId());

        // 分配默认角色
        Role defaultRole = roleRepository.findByName("ROLE_USER")
                .orElseGet(() -> {
                    log.info("ROLE_USER角色不存在，创建默认角色");
                    Role role = new Role();
                    role.setName("ROLE_USER");
                    role.setDescription("普通用户角色");
                    role.setEnabled(true);
                    return roleRepository.save(role);
                });

        UserRole userRole = new UserRole();
        userRole.setUser(user);
        userRole.setRole(defaultRole);
        userRoleRepository.save(userRole);
        log.info("用户角色分配成功，用户ID: {}，角色: {}", user.getId(), defaultRole.getName());

        // 同步用户数据到用户服务
        try {
            log.info("开始同步用户数据到用户服务，用户ID: {}", user.getId());
            UserServiceClient.SyncUserRequest syncRequest = new UserServiceClient.SyncUserRequest(
                    user.getId(),
                    user.getUsername(),
                    user.getEmail(),
                    user.getPhoneNumber(),
                    user.getNickName(),
                    user.getAddress()
            );
            Result<String> syncResult = userServiceClient.syncUser(syncRequest);
            if (syncResult != null && syncResult.getCode() == 200) {
                log.info("用户数据同步成功，用户ID: {}", user.getId());
            } else {
                log.error("用户数据同步失败，用户ID: {}，返回结果: {}", user.getId(), syncResult);
            }
        } catch (Exception e) {
            log.error("同步用户数据到用户服务异常，用户ID: {}，错误: {}", user.getId(), e.getMessage(), e);
            // 同步失败不影响注册流程，只记录日志
        }

        // 获取用户角色
        List<String> roles = Collections.singletonList(defaultRole.getName());

        // 生成令牌
        String token = jwtUtil.generateToken(user.getUsername(), user.getId(), roles, user.getUserType().name());
        String refreshToken = jwtUtil.generateRefreshToken(user.getUsername(), user.getId());
        
        log.info("用户注册流程完成，用户ID: {}，用户名: {}", user.getId(), user.getUsername());

        return new AuthResponse(
                token,
                refreshToken,
                user.getId(),
                user.getUsername(),
                user.getEmail(),
                user.getPhoneNumber(),
                user.getNickName(),
                user.getAddress(),
                user.getAvatarUrl(),
                user.getGender().name(),
                user.getLastLoginTime(),
                roles,
                user.getUserType().name()
        );
    }

/**
     * 刷新访问令牌
     * 
     * 刷新令牌流程：
     * 1. 验证刷新令牌的有效性
     * 2. 从令牌中提取用户名和用户ID
     * 3. 检查用户是否仍然存在于数据库中
     * 4. 获取用户的最新角色信息
     * 5. 生成新的访问令牌和刷新令牌
     * 6. 返回包含新令牌的认证响应
     * 
     * 安全考虑：
     * - 刷新令牌必须有效且未过期
     * - 用户必须存在于数据库中
     * - 每次刷新都会生成新的刷新令牌，增强安全性
     * - 令牌包含用户类型信息，用于权限控制
     * 
     * 性能优化：
     * - 使用数据库查询获取最新用户信息
     * - 角色信息实时查询，确保权限的准确性
     * - 令牌生成使用优化的JWT工具类
     * 
     * @param refreshToken 刷新令牌字符串
     * @return AuthResponse 包含新令牌的认证响应
     * @throws RuntimeException 当刷新令牌无效或用户不存在时抛出
     */
    @Override
    public AuthResponse refreshToken(String refreshToken) {
        log.info("开始刷新令牌流程");
        
        // 验证刷新令牌
        if (jwtUtil.isInvalid(refreshToken)) {
            log.warn("刷新令牌验证失败");
            throw new RuntimeException("刷新令牌无效");
        }

        // 从刷新令牌中获取用户名
        String username = jwtUtil.getUsername(refreshToken);
        Long userId = jwtUtil.getUserId(refreshToken);
        
        log.info("刷新令牌验证成功，用户名: {}，用户ID: {}", username, userId);

        // 检查用户是否存在
        User user = userRepository.findById(userId)
                .orElseThrow(() -> {
                    log.warn("用户不存在，用户ID: {}", userId);
                    return new RuntimeException("用户不存在");
                });

        // 获取用户角色
        List<String> roles = userRoleRepository.findByUserId(userId).stream()
                .map(userRole -> userRole.getRole().getName())
                .collect(Collectors.toList());
        
        log.info("获取用户角色成功，用户ID: {}，角色数量: {}", userId, roles.size());

        // 生成新的访问令牌
        String newToken = jwtUtil.generateToken(username, userId, roles, user.getUserType().name());

        // 生成新的刷新令牌
        String newRefreshToken = jwtUtil.generateRefreshToken(username, userId);
        
        log.info("令牌刷新成功，用户ID: {}", userId);

        return new AuthResponse(
                newToken,
                newRefreshToken,
                user.getId(),
                user.getUsername(),
                user.getEmail(),
                user.getPhoneNumber(),
                user.getNickName(),
                user.getAddress(),
                user.getAvatarUrl(),
                user.getGender().name(),
                user.getLastLoginTime(),
                roles,
                user.getUserType().name()
        );
    }

    /**
     * 用户登出
     * 
     * 登出流程详细说明：
     * 1. 验证访问令牌的有效性
     * 2. 从令牌中提取用户名和用户ID
     * 3. 将访问令牌加入黑名单，防止重复使用
     * 4. 清除用户的刷新令牌记录
     * 5. 记录用户登出日志
     * 
     * 安全考虑：
     * - 登出时必须提供有效的访问令牌
     * - 令牌加入黑名单后无法再次使用
     * - 清除刷新令牌防止通过刷新令牌重新获取访问令牌
     * - 记录登出日志用于安全审计
     * 
     * 黑名单机制：
     * - 使用JWT工具类的invalidateToken方法将令牌标记为无效
     * - 黑名单令牌会被后续的验证逻辑拒绝
     * - 黑名单通常存储在Redis等高速缓存中
     * 
     * 刷新令牌清理：
     * - 通过refreshTokenService删除用户的所有刷新令牌记录
     * - 确保用户无法通过旧的刷新令牌获取新的访问令牌
     * - 清理操作是幂等的，多次调用不会产生副作用
     * 
     * @param token 访问令牌字符串
     * @throws RuntimeException 当令牌无效时抛出
     */
    @Override
    public void logout(String token) {
        log.info("开始用户登出流程");
        
        // 验证令牌
        if (jwtUtil.isInvalid(token)) {
            log.warn("登出失败，令牌无效");
            throw new RuntimeException("令牌无效");
        }

        // 获取用户名
        String username = jwtUtil.getUsername(token);
        Long userId = jwtUtil.getUserId(token);
        
        log.info("用户登出验证成功，用户名: {}，用户ID: {}", username, userId);

        // 将令牌加入黑名单
        jwtUtil.invalidateToken(token);
        log.info("访问令牌已加入黑名单，用户ID: {}", userId);

        // 记录登出日志
        log.info("用户登出成功: {}", username);
    }

    /**
     * 修改用户密码
     * 
     * 密码修改流程详细说明：
     * 1. 根据用户ID查询用户信息
     * 2. 验证旧密码的正确性
     * 3. 验证新密码和确认密码的一致性
     * 4. 使用BCrypt加密新密码并更新到数据库
     * 5. 记录密码修改日志
     * 
     * 安全考虑：
     * - 旧密码验证防止未授权访问
     * - 新密码和确认密码必须完全一致
     * - 密码使用BCrypt算法进行单向加密存储
     * - 记录操作日志用于安全审计
     * 
     * 密码验证逻辑：
     * - 使用PasswordEncoder的matches方法验证旧密码
     * - 比较新密码和确认密码的字符串内容
     * - 验证失败时抛出RuntimeException异常
     * 
     * 数据更新：
     * - 使用BCrypt加密新密码
     * - 更新用户实体中的password字段
     * - 通过userRepository保存到数据库
     * - 操作完成后记录成功日志
     * 
     * @param userId 用户ID
     * @param request 密码修改请求对象，包含旧密码、新密码和确认密码
     * @throws RuntimeException 当用户不存在、旧密码错误或密码不一致时抛出
     */
    @Override
    public void changePassword(Long userId, ChangePasswordRequest request) {
        log.info("开始密码修改流程");
        
        // 获取用户信息
        User user = userRepository.findById(userId)
                .orElseThrow(() -> {
                    log.warn("密码修改失败，用户不存在，用户ID: {}", userId);
                    return new RuntimeException("用户不存在");
                });
        
        log.info("用户验证成功，用户名: {}", user.getUsername());

        // 验证旧密码
        if (!passwordEncoder.matches(request.getOldPassword(), user.getPassword())) {
            log.warn("密码修改失败，旧密码错误，用户: {}", user.getUsername());
            throw new RuntimeException("旧密码错误");
        }

        // 验证新密码和确认密码是否一致
        if (!request.getNewPassword().equals(request.getConfirmPassword())) {
            log.warn("密码修改失败，新密码和确认密码不一致，用户: {}", user.getUsername());
            throw new RuntimeException("新密码和确认密码不一致");
        }

        // 更新密码
        user.setPassword(passwordEncoder.encode(request.getNewPassword()));
        userRepository.save(user);
        
        log.info("密码更新成功，用户: {}", user.getUsername());

        // 记录密码修改日志
        log.info("用户修改密码成功: {}", user.getUsername());
    }

    /**
     * 更新用户个人资料
     * 
     * 资料更新流程详细说明：
     * 1. 根据用户ID查询用户信息
     * 2. 根据请求参数更新用户的基本信息
     * 3. 保存更新后的用户信息到数据库
     * 4. 通过Feign客户端同步数据到用户服务
     * 
     * 更新策略：
     * - 选择性更新：只更新非null的字段
     * - 支持部分更新：用户可以选择性提供需要更新的字段
     * - 保持原有数据：未提供的字段保持原值不变
     * - 实时生效：更新后立即保存到数据库
     * 
     * 可更新字段：
     * - nickName：用户昵称
     * - phoneNumber：手机号码
     * - email：邮箱地址
     * - address：联系地址
     * - avatarUrl：头像URL
     * 
     * 数据验证：
     * - 在DTO层通过注解进行基本的数据验证
     * - 手机号格式验证（如果提供）
     * - 邮箱格式验证（如果提供）
     * - 地址长度限制（如果提供）
     * 
     * 跨服务同步：
     * - 通过UserServiceClient同步更新后的用户数据
     * - 采用异步处理方式，不影响主流程
     * - 同步失败时记录错误日志，不影响更新成功
     * 
     * @param userId 用户ID
     * @param request 用户资料更新请求对象，包含需要更新的字段
     * @throws RuntimeException 当用户不存在时抛出
     */
    @Override
    public void updateProfile(Long userId, UpdateProfileRequest request) {
        log.info("开始用户资料更新流程");
        
        // 获取用户信息
        User user = userRepository.findById(userId)
                .orElseThrow(() -> {
                    log.warn("资料更新失败，用户不存在，用户ID: {}", userId);
                    return new RuntimeException("用户不存在");
                });
        
        log.info("用户验证成功，用户名: {}，开始更新资料", user.getUsername());

        // 更新用户信息（选择性更新，只更新非null字段）
        if (request.getNickName() != null && !request.getNickName().trim().isEmpty()) {
            user.setNickName(request.getNickName().trim());
            log.info("更新昵称: {}", request.getNickName());
        }
        if (request.getPhoneNumber() != null && !request.getPhoneNumber().trim().isEmpty()) {
            user.setPhoneNumber(request.getPhoneNumber().trim());
            log.info("更新手机号: {}", request.getPhoneNumber());
        }
        if (request.getEmail() != null && !request.getEmail().trim().isEmpty()) {
            user.setEmail(request.getEmail().trim());
            log.info("更新邮箱: {}", request.getEmail());
        }
        if (request.getAddress() != null && !request.getAddress().trim().isEmpty()) {
            user.setAddress(request.getAddress().trim());
            log.info("更新地址: {}", request.getAddress());
        }
        if (request.getAvatarUrl() != null && !request.getAvatarUrl().trim().isEmpty()) {
            user.setAvatarUrl(request.getAvatarUrl().trim());
            log.info("更新头像URL: {}", request.getAvatarUrl());
        }

        // 保存用户信息
        user = userRepository.save(user);
        log.info("用户资料更新成功，用户ID: {}", user.getId());

        // 同步用户数据到用户服务
        try {
            log.info("开始同步用户数据到用户服务，用户ID: {}", user.getId());
            UserServiceClient.SyncUserRequest syncRequest = new UserServiceClient.SyncUserRequest(
                    user.getId(),
                    user.getUsername(),
                    user.getEmail(),
                    user.getPhoneNumber(),
                    user.getNickName(),
                    user.getAddress()
            );
            Result<String> syncResult = userServiceClient.syncUser(syncRequest);
            if (syncResult != null && syncResult.getCode() == 200) {
                log.info("用户数据同步成功，用户ID: {}", user.getId());
            } else {
                log.error("用户数据同步失败，用户ID: {}，返回结果: {}", user.getId(), syncResult);
            }
        } catch (Exception e) {
            log.error("同步用户数据到用户服务异常，用户ID: {}，错误: {}", user.getId(), e.getMessage(), e);
            // 同步失败不影响主流程，只记录日志
        }

        log.info("用户资料更新流程完成，用户ID: {}", user.getId());
    }

    /**
     * 获取用户基本信息
     * 
     * 功能说明：
     * - 根据用户ID查询用户的基本信息
     * - 只返回必要的用户字段，不包含敏感信息
     * - 用于服务间的用户信息同步
     * 
     * 查询流程：
     * 1. 根据用户ID查询用户信息
     * 2. 将用户信息转换为UserInfoResponse对象
     * 3. 返回用户基本信息，不包含敏感字段
     * 
     * 返回字段：
     * - 用户ID、用户名、邮箱、手机号
     * - 用户昵称、头像URL、联系地址
     * - 用户类型、账户状态
     * - 创建时间、更新时间
     * 
     * 安全考虑：
     * - 不包含密码等敏感信息
     * - 只返回必要的用户字段
     * - 用户不存在时抛出异常
     * 
     * @param userId 用户ID
     * @return UserInfoResponse 用户基本信息响应对象
     * @throws RuntimeException 当用户不存在时抛出异常
     */
    @Override
    public UserInfoResponse getUserInfo(Long userId) {
        log.info("开始获取用户基本信息，用户ID: {}", userId);
        
        // 查询用户信息
        User user = userRepository.findById(userId)
                .orElseThrow(() -> {
                    log.warn("获取用户基本信息失败，用户不存在，用户ID: {}", userId);
                    return new RuntimeException("用户不存在");
                });
        
        log.info("用户查询成功，用户名: {}", user.getUsername());

        // 构建响应对象
        UserInfoResponse response = new UserInfoResponse(
                user.getId(),
                user.getUsername(),
                user.getEmail(),
                user.getPhoneNumber(),
                user.getNickName(),
                user.getAvatarUrl(),
                user.getAddress(),
                user.getUserType().name(),
                user.getEnabled(),
                user.getCreatedAt(),
                user.getUpdatedAt()
        );

        log.info("用户基本信息构建成功，用户ID: {}", userId);
        return response;
    }

    /**
     * 更新用户状态
     * 
     * 功能说明：
     * - 更新用户的启用/禁用状态
     * - 用于管理员控制用户账户状态
     * - 状态变更立即生效
     * 
     * 使用场景：
     * - 管理员禁用违规用户账户
     * - 管理员重新启用被禁用的账户
     * - 用户服务同步用户状态变更
     * 
     * 安全考虑：
     * - 状态变更需要记录日志
     * - 禁用的用户无法登录系统
     * - 状态变更不影响现有令牌有效性
     * 
     * @param userId 用户ID
     * @param enabled 启用状态（true:启用，false:禁用）
     * @throws RuntimeException 当用户不存在时抛出异常
     */
    @Override
    @Transactional
    public void updateUserStatus(Long userId, Boolean enabled) {
        log.info("开始更新用户状态，用户ID: {}，状态: {}", userId, enabled);
        
        // 查询用户信息
        User user = userRepository.findById(userId)
                .orElseThrow(() -> {
                    log.warn("更新用户状态失败，用户不存在，用户ID: {}", userId);
                    return new RuntimeException("用户不存在");
                });
        
        // 更新用户状态
        user.setEnabled(enabled);
        userRepository.save(user);
        
        log.info("用户状态更新成功，用户ID: {}，用户名: {}，新状态: {}", userId, user.getUsername(), enabled);
    }

    /**
     * 删除用户账户
     * 
     * 功能说明：
     * - 根据用户名删除用户账户
     * - 用于用户服务注册失败时的回滚操作
     * - 删除用户相关的所有数据（用户信息、角色关联等）
     * 
     * 使用场景：
     * - 用户服务注册失败时的补偿操作
     * - 管理员删除用户账户
     * - 系统维护时的数据清理
     * 
     * 安全考虑：
     * - 只允许服务间调用，不对外暴露
     * - 删除操作不可逆，需要谨慎使用
     * - 记录删除日志，用于审计追踪
     * 
     * @param username 用户名，标识要删除的用户
     * @throws RuntimeException 当用户不存在或删除失败时抛出异常
     */
    @Override
    @Transactional
    public void deleteUserByUsername(String username) {
        log.info("开始删除用户账户，用户名: {}", username);
        
        // 查询用户信息
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> {
                    log.warn("删除用户失败，用户不存在，用户名: {}", username);
                    return new RuntimeException("用户不存在");
                });
        
        log.info("用户查询成功，用户ID: {}，用户名: {}", user.getId(), user.getUsername());
        
        // 删除用户角色关联
        try {
            log.info("开始删除用户角色关联，用户ID: {}", user.getId());
            userRoleRepository.deleteByUserId(user.getId());
            log.info("用户角色关联删除成功，用户ID: {}", user.getId());
        } catch (Exception e) {
            log.error("删除用户角色关联失败，用户ID: {}，错误: {}", user.getId(), e.getMessage());
            throw new RuntimeException("删除用户角色关联失败");
        }
        
        // 删除用户记录
        try {
            log.info("开始删除用户记录，用户ID: {}", user.getId());
            userRepository.delete(user);
            log.info("用户记录删除成功，用户ID: {}", user.getId());
        } catch (Exception e) {
            log.error("删除用户记录失败，用户ID: {}，错误: {}", user.getId(), e.getMessage());
            throw new RuntimeException("删除用户记录失败");
        }
        
        log.info("用户账户删除成功，用户名: {}", username);
        
        // 发送用户删除事件到Kafka，通知其他微服务同步删除
        try {
            log.info("开始发送用户删除事件到Kafka，用户ID: {}，用户名: {}", user.getId(), user.getUsername());
            UserDeletedEvent event = new UserDeletedEvent(
                user.getId(),
                user.getUsername(),
                "SYSTEM", // 可以从当前认证信息中获取实际操作者
                "用户账户删除",
                "AUTH_SERVICE"
            );
            userDeletedEventProducer.sendUserDeletedEvent(event);
            log.info("用户删除事件发送成功，用户ID: {}，用户名: {}", user.getId(), user.getUsername());
        } catch (Exception e) {
            log.error("发送用户删除事件失败，用户ID: {}，用户名: {}，错误: {}", user.getId(), user.getUsername(), e.getMessage());
            // 事件发送失败不影响主流程，只记录日志
        }
    }

    /**
     * 根据用户ID删除用户账户
     * 
     * 功能说明：
     * - 根据用户ID删除用户账户及相关数据
     * - 用于管理员删除指定用户
     * - 删除用户相关的所有数据（用户信息、角色关联、令牌等）
     * 
     * 删除流程：
     * 1. 根据用户ID查询用户信息
     * 2. 删除用户角色关联数据
     * 3. 删除用户记录
     * 4. 记录删除日志
     * 
     * 使用场景：
     * - 管理员删除违规用户账户
     * - 用户注销账户
     * - 系统维护时的数据清理
     * 
     * 安全考虑：
     * - 只允许管理员操作，需要权限验证
     * - 删除操作不可逆，需要谨慎使用
     * - 记录删除日志，用于审计追踪
     * 
     * @param userId 用户ID，标识要删除的用户
     * @throws RuntimeException 当用户不存在或删除失败时抛出异常
     */
    @Override
    @Transactional
    public void deleteUserById(Long userId) {
        log.info("开始删除用户账户，用户ID: {}", userId);
        
        // 查询用户信息
        User user = userRepository.findById(userId)
                .orElseThrow(() -> {
                    log.warn("删除用户失败，用户不存在，用户ID: {}", userId);
                    return new RuntimeException("用户不存在");
                });
        
        log.info("用户查询成功，用户ID: {}，用户名: {}", user.getId(), user.getUsername());
        
        // 删除用户角色关联
        try {
            log.info("开始删除用户角色关联，用户ID: {}", user.getId());
            userRoleRepository.deleteByUserId(user.getId());
            log.info("用户角色关联删除成功，用户ID: {}", user.getId());
        } catch (Exception e) {
            log.error("删除用户角色关联失败，用户ID: {}，错误: {}", user.getId(), e.getMessage());
            throw new RuntimeException("删除用户角色关联失败");
        }
        
        // 删除用户记录
        try {
            log.info("开始删除用户记录，用户ID: {}", user.getId());
            userRepository.delete(user);
            log.info("用户记录删除成功，用户ID: {}", user.getId());
        } catch (Exception e) {
            log.error("删除用户记录失败，用户ID: {}，错误: {}", user.getId(), e.getMessage());
            throw new RuntimeException("删除用户记录失败");
        }
        
        log.info("用户账户删除成功，用户ID: {}，用户名: {}", userId, user.getUsername());
        
        // 发送用户删除事件到Kafka，通知其他微服务同步删除
        try {
            log.info("开始发送用户删除事件到Kafka，用户ID: {}，用户名: {}", user.getId(), user.getUsername());
            UserDeletedEvent event = new UserDeletedEvent(
                user.getId(),
                user.getUsername(),
                "SYSTEM", // 可以从当前认证信息中获取实际操作者
                "用户账户删除",
                "AUTH_SERVICE"
            );
            userDeletedEventProducer.sendUserDeletedEvent(event);
            log.info("用户删除事件发送成功，用户ID: {}，用户名: {}", user.getId(), user.getUsername());
        } catch (Exception e) {
            log.error("发送用户删除事件失败，用户ID: {}，用户名: {}，错误: {}", user.getId(), user.getUsername(), e.getMessage());
            // 事件发送失败不影响主流程，只记录日志
        }
    }

    /**
     * 批量删除用户账户
     * 
     * 功能说明：
     * - 批量删除多个用户账户及相关数据
     * - 用于管理员批量删除用户
     * - 删除用户相关的所有数据（用户信息、角色关联、令牌等）
     * 
     * 删除流程：
     * 1. 遍历用户ID列表，查询每个用户信息
     * 2. 批量删除用户角色关联数据
     * 3. 批量删除用户记录
     * 4. 记录删除日志
     * 
     * 使用场景：
     * - 管理员批量清理违规用户
     * - 系统维护时的批量数据清理
     * - 用户批量注销
     * 
     * 安全考虑：
     * - 只允许管理员操作，需要权限验证
     * - 删除操作不可逆，需要谨慎使用
     * - 记录删除日志，用于审计追踪
     * - 支持事务处理，确保数据一致性
     * 
     * @param userIds 用户ID列表，标识要删除的用户
     * @throws RuntimeException 当删除失败时抛出异常
     */
    @Override
    @Transactional
    public void batchDeleteUsers(List<Long> userIds) {
        log.info("开始批量删除用户账户，用户数量: {}", userIds.size());
        
        if (userIds == null || userIds.isEmpty()) {
            log.warn("批量删除用户失败，用户ID列表为空");
            throw new RuntimeException("用户ID列表不能为空");
        }
        
        int successCount = 0;
        int failCount = 0;
        
        for (Long userId : userIds) {
            try {
                log.info("开始删除用户，用户ID: {}", userId);
                
                // 查询用户信息
                User user = userRepository.findById(userId)
                        .orElseThrow(() -> {
                            log.warn("删除用户失败，用户不存在，用户ID: {}", userId);
                            return new RuntimeException("用户不存在");
                        });
                
                log.info("用户查询成功，用户ID: {}，用户名: {}", user.getId(), user.getUsername());
                
                // 删除用户角色关联
                try {
                    log.info("开始删除用户角色关联，用户ID: {}", user.getId());
                    userRoleRepository.deleteByUserId(user.getId());
                    log.info("用户角色关联删除成功，用户ID: {}", user.getId());
                } catch (Exception e) {
                    log.error("删除用户角色关联失败，用户ID: {}，错误: {}", user.getId(), e.getMessage());
                    throw new RuntimeException("删除用户角色关联失败");
                }
                
                // 删除用户记录
                try {
                    log.info("开始删除用户记录，用户ID: {}", user.getId());
                    userRepository.delete(user);
                    log.info("用户记录删除成功，用户ID: {}", user.getId());
                    successCount++;
                } catch (Exception e) {
                    log.error("删除用户记录失败，用户ID: {}，错误: {}", user.getId(), e.getMessage());
                    throw new RuntimeException("删除用户记录失败");
                }
                
                log.info("用户删除成功，用户ID: {}，用户名: {}", userId, user.getUsername());
                
            } catch (Exception e) {
                log.error("删除用户失败，用户ID: {}，错误: {}", userId, e.getMessage());
                failCount++;
                // 继续处理下一个用户，不抛出异常
            }
        }
        
        log.info("批量删除用户完成，总数量: {}，成功: {}，失败: {}", userIds.size(), successCount, failCount);
        
        // 发送批量用户删除事件到Kafka
        try {
            log.info("开始发送批量用户删除事件到Kafka，成功删除用户数量: {}", successCount);
            for (Long userId : userIds) {
                try {
                    // 重新查询用户信息以获取用户名
                    User user = userRepository.findById(userId).orElse(null);
                    if (user != null) {
                        UserDeletedEvent event = new UserDeletedEvent(
                            user.getId(),
                            user.getUsername(),
                            "SYSTEM",
                            "批量用户删除",
                            "AUTH_SERVICE"
                        );
                        userDeletedEventProducer.sendUserDeletedEvent(event);
                        log.info("批量删除事件发送成功，用户ID: {}，用户名: {}", user.getId(), user.getUsername());
                    }
                } catch (Exception e) {
                    log.error("发送单个用户删除事件失败，用户ID: {}，错误: {}", userId, e.getMessage());
                    // 单个事件发送失败不影响其他事件
                }
            }
            log.info("批量用户删除事件发送完成，成功发送事件数量: {}", successCount);
        } catch (Exception e) {
            log.error("发送批量用户删除事件失败，错误: {}", e.getMessage());
            // 批量事件发送失败不影响主流程，只记录日志
        }
        
        // 如果全部失败，则抛出异常
        if (failCount == userIds.size()) {
            throw new RuntimeException("批量删除用户失败，所有用户都删除失败");
        }
        
        // 如果部分失败，记录警告但不抛出异常
        if (failCount > 0) {
            log.warn("批量删除用户部分失败，总数量: {}，成功: {}，失败: {}", userIds.size(), successCount, failCount);
        }
    }

    /**
     * 检查用户名是否存在
     * 
     * 功能说明：
     * - 根据用户名检查用户是否存在于认证服务中
     * - 用于用户删除事件的同步处理
     * - 支持跨服务用户存在性验证
     * 
     * @param username 用户名
     * @return boolean 用户存在性检查结果
     */
    @Override
    public boolean existsByUsername(String username) {
        log.info("检查用户名是否存在: {}", username);
        
        try {
            boolean exists = userRepository.existsByUsername(username);
            log.info("用户名存在性检查结果 - 用户名: {}, 存在: {}", username, exists);
            return exists;
        } catch (Exception e) {
            log.error("检查用户名存在性失败 - 用户名: {}, 错误: {}", username, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 服务登录
     * 
     * 功能说明：
     * - 用于微服务间调用认证服务获取访问令牌
     * - 验证服务身份并返回有效的JWT令牌
     * - 支持服务间安全通信
     * 
     * 验证流程：
     * 1. 验证服务ID和服务密钥的合法性
     * 2. 检查服务是否已注册且状态正常
     * 3. 生成专用于服务间调用的JWT令牌
     * 4. 返回令牌信息，包含有效期
     * 
     * 安全考虑：
     * - 服务密钥需要安全存储和传输
     * - 令牌具有较短的有效期，建议1小时
     * - 令牌包含服务标识，便于权限控制
     * - 支持令牌黑名单机制
     * 
     * @param request 服务登录请求，包含serviceId和serviceKey
     * @return ServiceLoginResponse 服务登录响应，包含访问令牌和有效期
     * @throws RuntimeException 当服务ID或密钥无效时抛出异常
     */
    @Override
    public ServiceLoginResponse serviceLogin(ServiceLoginRequest request) {
        log.info("开始服务登录验证，服务ID: {}", request.getServiceId());
        
        // 验证服务ID和服务密钥
        if (!validateServiceCredentials(request.getServiceId(), request.getServiceKey())) {
            log.warn("服务登录失败，服务ID或密钥无效，服务ID: {}", request.getServiceId());
            throw new RuntimeException("服务ID或密钥无效");
        }
        
        log.info("服务身份验证成功，服务ID: {}", request.getServiceId());
        
        // 生成服务间调用的JWT令牌
        String token = jwtUtil.generateServiceToken(request.getServiceId());
        
        // 设置令牌有效期为1小时（3600秒）
        Long expiresIn = 3600L;
        
        // 构建响应对象
        ServiceLoginResponse response = new ServiceLoginResponse();
        response.setToken(token);
        response.setTokenType("Bearer");
        response.setExpiresIn(expiresIn);
        response.setServiceId(request.getServiceId());
        
        log.info("服务登录成功，服务ID: {}，令牌有效期: {}秒", request.getServiceId(), expiresIn);
        
        return response;
    }
    
    /**
     * 验证服务凭据
     * 
     * 功能说明：
     * - 验证服务ID和服务密钥的合法性
     * - 检查服务是否已注册且状态正常
     * 
     * 实现说明：
     * - 当前实现为简化版本，实际生产环境应该从数据库或配置中心获取服务信息
     * - 支持多个微服务的服务ID和密钥配置
     * - 可以扩展支持服务状态管理和动态配置
     * 
     * @param serviceId 服务ID
     * @param serviceKey 服务密钥
     * @return boolean 验证结果
     */
    private boolean validateServiceCredentials(String serviceId, String serviceKey) {
        log.debug("验证服务凭据，服务ID: {}", serviceId);
        
        // 简化实现：硬编码验证用户服务的凭据
        // 实际生产环境应该从数据库或配置中心获取服务信息
        if ("user-service".equals(serviceId) && "user-service-secret-key-12345".equals(serviceKey)) {
            return true;
        }
        
        // 可以添加更多服务的凭据验证
        // 例如：订单服务、商品服务等
        
        log.debug("服务凭据验证失败，服务ID: {}", serviceId);
        return false;
    }
}